using System;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Linq;
using System.Windows.Input;
using System.Windows.Media;

namespace Supremacy.Client.Controls
{
    public class GamePopupSite : Decorator
    {
        #region Fields
        private readonly GamePopupCollection _popups;
        private readonly Canvas _canvas;
        private readonly GamePopupCollection _openPopupsCore;
        private readonly ReadOnlyGamePopupCollection _openPopups;
        #endregion

        #region Constructors and Finalizers
        public GamePopupSite()
        {
            _popups = new GamePopupCollection();
            _popups.CollectionChanged += OnPopupsCollectionChanged;

            _canvas = new Canvas();

            this.AddVisualChild(_canvas);
            this.AddLogicalChild(_canvas);

            _openPopupsCore = new GamePopupCollection();
            _openPopups = new ReadOnlyGamePopupCollection(_popups);

            AddHandler(LoadedEvent, (RoutedEventHandler)OnLoaded);
        }

        private void OnPopupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            ProcessPopupsCollectionChanged(e);
        }

        private void UpdateRegisteredPopupSite(GamePopup popup, bool register)
        {
            if (register)
            {
                if (popup.RegisteredPopupSite != this)
                {
                    if (popup.RegisteredPopupSite != null)
                        throw new InvalidOperationException("GamePopup is already registered with another GamePopupSite.");
                    popup.RegisteredPopupSite = this;
                }
            }
            else if (popup.RegisteredPopupSite == this)
            {
                popup.RegisteredPopupSite = null;
            }
        }

        private void ProcessPopupsCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                    foreach (var item in e.NewItems)
                    {
                        var gamePopup = item as GamePopup;
                        if (gamePopup != null)
                            this.UpdateRegisteredPopupSite(gamePopup, true);
                    }
                    break;

                case NotifyCollectionChangedAction.Reset:
                    if (!DesignerProperties.GetIsInDesignMode(this))
                        throw new NotSupportedException();
                    break;

                case NotifyCollectionChangedAction.Remove:
                    foreach (var item in e.OldItems)
                    {
                        var gamePopup = item as GamePopup;
                        if (gamePopup == null)
                            continue;
                        if (gamePopup.IsOpen)
                            gamePopup.IsOpen = false;
                        this.UpdateRegisteredPopupSite(gamePopup, false);
                    }
                    break;

                case NotifyCollectionChangedAction.Replace:
                    if (!DesignerProperties.GetIsInDesignMode(this))
                        throw new NotSupportedException();
                    break;
            }
        }
        #endregion

        #region Properties

        #region HasOpenPopups Property
        public bool HasOpenPopups
        {
            get { return _openPopups.Any(); }
        }
        #endregion

        #region OpenPopups Property
        public ReadOnlyGamePopupCollection OpenPopups
        {
            get { return _openPopups; }
        }
        #endregion

        #region Popups Property
        public GamePopupCollection Popups
        {
            get { return _popups; }
        }
        #endregion

        #endregion

        #region Internal Methods
        internal void AddCanvasChild(UIElement element)
        {
            if (!_canvas.Children.Contains(element))
                _canvas.Children.Add(element);
        }

        internal void BringToFront(UIElement element)
        {
            var zIndex = 0;

            foreach (var child in _canvas.Children.OfType<UIElement>())
            {
                if (child is GamePopup)
                    zIndex = Math.Max(zIndex, Panel.GetZIndex(child));
            }

            Panel.SetZIndex(element, zIndex + 1);
        }

        internal void RemoveCanvasChild(UIElement element)
        {
            if (_canvas.Children.Contains(element))
                _canvas.Children.Remove(element);
        }

        internal void UpdateOpenGamePopups()
        {
            _openPopupsCore.BeginUpdate();
            try
            {
                _openPopupsCore.Clear();
                _openPopupsCore.AddRange(_popups.Where(o => o.IsOpen));
            }
            finally
            {
                _openPopupsCore.EndUpdate();
            }
        }
        #endregion

        #region Private Methods
        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            this.UpdateOpenGamePopups();
            CommandManager.InvalidateRequerySuggested();
        }
        #endregion

        #region Visual Child Enumeration
        protected override int VisualChildrenCount
        {
            get { return base.VisualChildrenCount + 1; }
        }

        protected override Visual GetVisualChild(int index)
        {
            if (index < base.VisualChildrenCount)
                return base.GetVisualChild(index);
            return _canvas;
        }
        #endregion

        #region Measure and Arrange Overrides
        protected override Size ArrangeOverride(Size finalSize)
        {
            _canvas.Arrange(new Rect(new Point(), finalSize));
            return base.ArrangeOverride(finalSize);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            _canvas.Measure(constraint);
            return base.MeasureOverride(constraint);
        }
        #endregion
    }
}